//
// Created by 49647 on 2021/7/3.
//
#include <iostream>
#include <type_traits>
#include <optional>
#include <memory>
#include <string>

using namespace std;

class NullOptType {
};

NullOptType nullOpt;

template<typename T, enable_if_t<!is_reference_v<T>> * = nullptr>
class Optional {
public:
    Optional() = default;

    virtual ~Optional() = default;

    Optional(NullOptType) {}

    Optional(const T &v) {
        m_data = make_unique<T>(v);
        m_hasValue = true;
    }

    Optional(const Optional &v) {
        if (v.HasValue()) {
            m_data = make_unique<T>(Value());
            m_hasValue = true;
        }
    }

    bool HasValue() const {
        return m_hasValue;
    }

    explicit operator bool() const {
        return m_hasValue;
    }

    const T &Value() const {
        if (m_hasValue) {
            return *m_data;
        } else {
            throw std::exception();
        }
    }

    const T &operator*() const {
        return Value();
    }

    void Reset() {
        m_data.reset();
        m_hasValue = false;
    }

private:
    bool m_hasValue = false;
    unique_ptr<T> m_data = nullptr;
};

int main() {
    Optional<int> o1;
    if (o1) {
        printf("o1 has value\n");
    }
    Optional<int> o2 = nullOpt;
    if (o2) {
        printf("o2 has value\n");
    }
    Optional<int> o3 = 10;
    if (o3) {
        printf("o3 has value %d\n", o3.Value());
    }
    Optional<string> o4 = string("haha");
    if (o4.HasValue()) {
        printf("o4 has value %s\n", (*o4).c_str());
    }
    //Optional<int&> o5; 编译失败
}